-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement fuzz testing #97
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for this PR, this is very good and so helpful.
Added a few questions/comments. My biggest change request would be to streamline this to remove the parsec-interface/fuzz
feature (as my supposition is that it's no longer needed) and remove associated code change.
It could still be added later when other fuzz targets are added!
cargo +nightly fuzz run fuzz_service | ||
|
||
# Notify about crash | ||
echo "Here we'd ping the webhook to notify" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we currently do not notify the crash, would it be easy to read the logs to find the crashes that happened? Could it be easier, for now, to remove the while true
loop and exit at the first crash?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be, but why not continue? the crash data (i.e. the input that triggered the crash) is saved by the fuzzer anyway. So far the only crash that happened was because the fuzzer kept using up more and more memory and died when it reached its limit - not exactly a meaningful crash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's good then! As long as it is easy to know when a crash happenened and with what data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't only record crashes, it also records slow runs
Cargo.toml
Outdated
@@ -49,3 +50,4 @@ default = ["mbed-crypto-provider", "pkcs11-provider"] | |||
mbed-crypto-provider = [] | |||
pkcs11-provider = ["pkcs11", "picky-asn1-der", "picky-asn1"] | |||
tpm-provider = ["tss-esapi", "picky-asn1-der", "picky-asn1"] | |||
fuzz = ["parsec-interface/fuzz", "arbitrary"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the parsec-interface/fuzz
feature still needed now? As we only pass a stream to the fuzzer and now longer a Request
+ ApplicationName
?
I guess as discussed in #54 it will be used when fuzzing individual components, but for now should we remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, i guess I can remove it and reintroduce when adding the other fuzzing stuff
fuzz/Cargo.toml
Outdated
[package] | ||
name = "parsec-fuzz" | ||
version = "0.0.0" | ||
authors = ["Automatically generated"] | ||
publish = false | ||
edition = "2018" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we care about filling those with the same values as in our other crates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wasn't sure about that... it was obviously automatically generated, I can update it to be something similar to the parsec Cargo.toml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll only update the authors, will leave version as "0.0.0"
docker logs -f --tail 100 $FUZZ_CONTAINER_NAME | ||
elif [[ "$1" == "clean" ]] | ||
then | ||
docker run -d --rm -v $(pwd):/parsec -w /parsec/fuzz --name $CLEANUP_CONTAINER_NAME parsec/fuzz ./cleanup.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup.sh
script seems to only have a dependency on the rm
command. Do we need to execute it inside the parsec/fuzz
container? We could also write the commands directly here and remove the script.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well yes, but no. if you try that on your server, in the parsec account, you'll see why :) when docker runs, it runs as root, so removing new files isn't possible from a non-sudoer account, unless you do something from a container again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a comment!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the info!
fuzz/fuzz_targets/fuzz_service.rs
Outdated
use parsec::authenticators::ApplicationName; | ||
use parsec::fuzz_utils::requests::Request; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these two use
are needed anymore. cf the comment on the parsec-interface-rs
fuzz
feature :)
#[derive(Arbitrary, Debug)] | ||
struct MockStream(Vec<u8>); | ||
|
||
impl Read for MockStream { | ||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
if self.0.is_empty() { | ||
return Ok(0); | ||
} | ||
let n = cmp::min(buf.len(), self.0.len()); | ||
for (idx, val) in self.0.drain(0..n).enumerate() { | ||
buf[idx] = val; | ||
} | ||
|
||
Ok(n) | ||
} | ||
} | ||
|
||
impl Write for MockStream { | ||
fn write(&mut self, buf: &[u8]) -> Result<usize> { | ||
Ok(buf.len()) | ||
} | ||
|
||
fn flush(&mut self) -> Result<()> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
fuzz_target!(|stream: MockStream| { | ||
FRONT_END_HANDLER.handle_request(stream); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is really awesome 💯
Because of the MockStream
Read + Write
interface and the modularity of the design this is amazing to see how easy it is to re-use almost everything in Parsec!
set -e | ||
set -x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we were discussing it and just FYI, I was reading this interesting article about safe shell scripting and the recommended variables to set on top of shell scripts. Not requesting any changes here.
src/authenticators/mod.rs
Outdated
#[cfg(feature = "fuzz")] | ||
use arbitrary::Arbitrary; | ||
use parsec_interface::requests::request::RequestAuth; | ||
use parsec_interface::requests::Result; | ||
|
||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are those still needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope!
src/lib.rs
Outdated
|
||
#[cfg(feature = "fuzz")] | ||
pub mod fuzz_utils { | ||
pub use parsec_interface::*; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here: is it still needed?
if [[ "$1" == "run" ]] | ||
then | ||
# Set up fuzz folder | ||
cp fuzz/config.toml fuzz/run_config.toml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it needed to copy the configuration file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so that you don't have to remove the PKCS11 slot number after each use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah makes sense! Maybe a comment would help here then
This commit implements fuzz testing through libFuzzer and the framework around it to continuously run the fuzzer in a Docker container, along with a service that checks for updates on the repo. The service builds the Docker image and launches the fuzzer, after which it waits for any update to the service. Given that the fuzzing corpus will remain in-place, whenever fuzzing is restarted for another service version, it will take off from the already existing cases. The Docker image has all the required components to run all the current providers - Mbed, PKCS11 and TPM - and to continuously fuzz the service through a stub version of the frontend handler. If a crash is detected, the fuzzer will notify the team and continue fuzzing. Signed-off-by: Ionut Mihalcea <ionut.mihalcea@arm.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes! Looking good
This commit implements fuzz testing through libFuzzer and the framework around it
to continuously run the fuzzer in a Docker container, along with a service that
checks for updates on the repo.
The service builds the Docker image and launches the fuzzer, after which it waits
for any update to the service. Given that the fuzzing corpus will remain in-place,
whenever fuzzing is restarted for another service version, it will take off from the
already existing cases.
The Docker image has all the required components to run all the current providers -
Mbed, PKCS11 and TPM - and to continuously fuzz the service through a stub version of
the frontend handler. If a crash is detected, the fuzzer will notify the team and
continue fuzzing.